package com.toolkit.module.calculation.service.impl;

import com.toolkit.module.calculation.bean.LoanInfo;
import com.toolkit.module.calculation.dto.CommercialLoanDto;
import com.toolkit.module.calculation.dto.PrepaymentLoanDto;
import com.toolkit.module.calculation.mapper.LoanInfoMapper;
import com.toolkit.module.calculation.service.ILoanInfoService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.toolkit.module.calculation.vo.CommercialLoanVo;
import com.toolkit.module.calculation.vo.PrepaymentLoanVo;
import com.toolkit.module.calculation.vo.RepaymentDetailVo;
import com.toolkit.util.EarlyRepaymentCalculator;
import com.toolkit.util.EarlyRepaymentCalculatorUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 贷款信息 服务实现类
 * </p>
 *
 * @author Maoco
 * @since 2025-06-10
 */
@Service
public class LoanInfoServiceImpl extends ServiceImpl<LoanInfoMapper, LoanInfo> implements ILoanInfoService {
    @Autowired
    private LoanInfoMapper loanInfoMapper;

    @Override
    public List<CommercialLoanVo> getCommercialLoanDetail(CommercialLoanDto commercialLoanDto) {
        LoanInfo loanInfo = new LoanInfo();
        BeanUtils.copyProperties(commercialLoanDto, loanInfo);
        loanInfoMapper.insert(loanInfo);
        List<CommercialLoanVo> commercialLoanVoList = new ArrayList<>();
        commercialLoanVoList.add(equalLoanPayment(commercialLoanDto.getCommercialLoanTotalAmount().doubleValue(), commercialLoanDto.getCommercialLoanLendingRate().doubleValue(), commercialLoanDto.getCommercialLoanTerm()));
        commercialLoanVoList.add(equalPrincipalInterest(commercialLoanDto.getCommercialLoanTotalAmount().doubleValue(), commercialLoanDto.getCommercialLoanLendingRate().doubleValue(), commercialLoanDto.getCommercialLoanTerm()));
        return commercialLoanVoList;
    }

    @Override
    public List<CommercialLoanVo> getZhLoanDetail(CommercialLoanDto commercialLoanDto) {
        LoanInfo loanInfo = new LoanInfo();
        BeanUtils.copyProperties(commercialLoanDto, loanInfo);
        loanInfoMapper.insert(loanInfo);
        List<CommercialLoanVo> commercialLoanVoList = new ArrayList<>();
        CommercialLoanVo commercialLoanEqualLoanVo =equalLoanPayment(commercialLoanDto.getCommercialLoanTotalAmount().doubleValue(), commercialLoanDto.getCommercialLoanLendingRate().doubleValue(), commercialLoanDto.getCommercialLoanTerm());
        commercialLoanEqualLoanVo.setType(1);
        CommercialLoanVo providentFundLoanEqualLoanVo =equalLoanPayment(commercialLoanDto.getProvidentFundLoanTotalAmount().doubleValue(), commercialLoanDto.getProvidentFundLoanLendingRate().doubleValue(), commercialLoanDto.getProvidentFundLoanTerm());
        providentFundLoanEqualLoanVo.setType(2);
        CommercialLoanVo totalLoanEqualLoanVov =totalLoan(commercialLoanEqualLoanVo,providentFundLoanEqualLoanVo,3);
        totalLoanEqualLoanVov.setRepaymentType(2);
        CommercialLoanVo commercialLoanEqualPrincipalVo =equalPrincipalInterest(commercialLoanDto.getCommercialLoanTotalAmount().doubleValue(), commercialLoanDto.getCommercialLoanLendingRate().doubleValue(), commercialLoanDto.getCommercialLoanTerm());
        commercialLoanEqualPrincipalVo.setType(4);
        CommercialLoanVo providentFundLoanEqualPrincipalVo =equalPrincipalInterest(commercialLoanDto.getProvidentFundLoanTotalAmount().doubleValue(), commercialLoanDto.getProvidentFundLoanLendingRate().doubleValue(), commercialLoanDto.getProvidentFundLoanTerm());
        providentFundLoanEqualPrincipalVo.setType(5);
        CommercialLoanVo totalLoanEqualPrincipalVo =totalLoan(commercialLoanEqualPrincipalVo,providentFundLoanEqualPrincipalVo,6);
        totalLoanEqualPrincipalVo.setRepaymentType(1);
        commercialLoanVoList.add(commercialLoanEqualLoanVo);
        commercialLoanVoList.add(providentFundLoanEqualLoanVo);
        commercialLoanVoList.add(totalLoanEqualLoanVov);
        commercialLoanVoList.add(commercialLoanEqualPrincipalVo);
        commercialLoanVoList.add(providentFundLoanEqualPrincipalVo);
        commercialLoanVoList.add(totalLoanEqualPrincipalVo);
        return commercialLoanVoList;
    }

    @Override
    public PrepaymentLoanVo getPrepaymentLoanDetail(PrepaymentLoanDto prepaymentLoanDto) {
        PrepaymentLoanVo prepaymentLoanVo=null;
        if(prepaymentLoanDto.getLoanType()==1){
            EarlyRepaymentCalculator calculator = new EarlyRepaymentCalculator(prepaymentLoanDto);
            if(prepaymentLoanDto.getFaType() ==0){
                prepaymentLoanVo=calculator.calculateEarlyRepayment();
            }else if(prepaymentLoanDto.getFaType() ==1){
                prepaymentLoanVo=calculator.calculateEarlyRepayment();
            }
        }else if(prepaymentLoanDto.getLoanType()==2){
            EarlyRepaymentCalculatorUtil calculator = new EarlyRepaymentCalculatorUtil(prepaymentLoanDto);
            if(prepaymentLoanDto.getFaType() ==0){
                prepaymentLoanVo=calculator.calculateEarlyRepayment();
            }else if(prepaymentLoanDto.getFaType() ==1){
                prepaymentLoanVo=calculator.calculateEarlyRepayment();
            }
        }
        return prepaymentLoanVo;
    }



    private CommercialLoanVo totalLoan(CommercialLoanVo commercialLoanEqualLoanVo, CommercialLoanVo providentFundLoanEqualLoanVo,int type) {
        CommercialLoanVo totalLoanEqualLoanVo=new CommercialLoanVo();
        totalLoanEqualLoanVo.setTotalAmount(new BigDecimal(commercialLoanEqualLoanVo.getTotalAmount()+providentFundLoanEqualLoanVo.getTotalAmount()).setScale(2,RoundingMode.HALF_UP).doubleValue());
        totalLoanEqualLoanVo.setType(type);
        Integer maxTrem=Math.max(commercialLoanEqualLoanVo.getLoanTerm(),providentFundLoanEqualLoanVo.getLoanTerm());
        totalLoanEqualLoanVo.setLoanTerm(maxTrem);
//        totalLoanEqualLoanVo.setInterestRate(commercialLoanEqualLoanVo.getInterestRate());
//        totalLoanEqualLoanVo.setRepaymentType(2);
        totalLoanEqualLoanVo.setMonthSalary(new BigDecimal(commercialLoanEqualLoanVo.getMonthSalary()+providentFundLoanEqualLoanVo.getMonthSalary()).setScale(2,RoundingMode.HALF_UP).doubleValue());
        if(type ==3){
            totalLoanEqualLoanVo.setDecreasingInterest(new BigDecimal(commercialLoanEqualLoanVo.getDecreasingInterest()+providentFundLoanEqualLoanVo.getDecreasingInterest()).setScale(2,RoundingMode.HALF_UP).doubleValue());
            if(commercialLoanEqualLoanVo.getLoanTerm()==providentFundLoanEqualLoanVo.getLoanTerm()) {
                totalLoanEqualLoanVo.setLastRePayment(new BigDecimal(commercialLoanEqualLoanVo.getLastRePayment() + providentFundLoanEqualLoanVo.getLastRePayment()).setScale(2, RoundingMode.HALF_UP).doubleValue());
            }else if(commercialLoanEqualLoanVo.getLoanTerm()>providentFundLoanEqualLoanVo.getLoanTerm()){
                totalLoanEqualLoanVo.setLastRePayment(new BigDecimal(commercialLoanEqualLoanVo.getLastRePayment() ).setScale(2, RoundingMode.HALF_UP).doubleValue());
            }else{
                totalLoanEqualLoanVo.setLastRePayment(new BigDecimal(providentFundLoanEqualLoanVo.getLastRePayment()).setScale(2, RoundingMode.HALF_UP).doubleValue());
            }
        }
        totalLoanEqualLoanVo.setTotalInterestAmount(new BigDecimal(commercialLoanEqualLoanVo.getTotalInterestAmount()+providentFundLoanEqualLoanVo.getTotalInterestAmount()).setScale(2,RoundingMode.HALF_UP).doubleValue());
        totalLoanEqualLoanVo.setTotalPrincipalAmount(new BigDecimal(commercialLoanEqualLoanVo.getTotalPrincipalAmount()+providentFundLoanEqualLoanVo.getTotalPrincipalAmount()).setScale(2,RoundingMode.HALF_UP).doubleValue());
        totalLoanEqualLoanVo.setFirstRePayment(new BigDecimal(commercialLoanEqualLoanVo.getFirstRePayment()+providentFundLoanEqualLoanVo.getFirstRePayment()).setScale(2,RoundingMode.HALF_UP).doubleValue());
        totalLoanEqualLoanVo.setAnnuaSalary(new BigDecimal(commercialLoanEqualLoanVo.getAnnuaSalary()+providentFundLoanEqualLoanVo.getAnnuaSalary()).setScale(2,RoundingMode.HALF_UP).doubleValue());
        List<RepaymentDetailVo> repaymentDetailVoList = new ArrayList<>();
        if(commercialLoanEqualLoanVo.getRepaymentList().size() >= providentFundLoanEqualLoanVo.getRepaymentList().size()){
            for(int i = 0;i<commercialLoanEqualLoanVo.getRepaymentList().size();i++){
                RepaymentDetailVo repaymentDetailVo=new RepaymentDetailVo();
                if(i<providentFundLoanEqualLoanVo.getRepaymentList().size()){
                    repaymentDetailVo.setPrincipalMonth(new BigDecimal(providentFundLoanEqualLoanVo.getRepaymentList().get(i).getPrincipalMonth()+commercialLoanEqualLoanVo.getRepaymentList().get(i).getPrincipalMonth()).setScale(2,RoundingMode.HALF_UP).doubleValue());
                    repaymentDetailVo.setInterestMonth(new BigDecimal(providentFundLoanEqualLoanVo.getRepaymentList().get(i).getInterestMonth()+commercialLoanEqualLoanVo.getRepaymentList().get(i).getInterestMonth()).setScale(2,RoundingMode.HALF_UP).doubleValue());
                    repaymentDetailVo.setTotalMonth(new BigDecimal(providentFundLoanEqualLoanVo.getRepaymentList().get(i).getTotalMonth()+commercialLoanEqualLoanVo.getRepaymentList().get(i).getTotalMonth()).setScale(2,RoundingMode.HALF_UP).doubleValue());
                    repaymentDetailVo.setRemainingPrincipal(new BigDecimal(providentFundLoanEqualLoanVo.getRepaymentList().get(i).getRemainingPrincipal()+commercialLoanEqualLoanVo.getRepaymentList().get(i).getRemainingPrincipal()).setScale(2,RoundingMode.HALF_UP).doubleValue());
                    repaymentDetailVo.setPeriod(i+1);
                }else{
                    repaymentDetailVo.setPrincipalMonth(new BigDecimal(commercialLoanEqualLoanVo.getRepaymentList().get(i).getPrincipalMonth()).setScale(2,RoundingMode.HALF_UP).doubleValue());
                    repaymentDetailVo.setInterestMonth(new BigDecimal(commercialLoanEqualLoanVo.getRepaymentList().get(i).getInterestMonth()).setScale(2,RoundingMode.HALF_UP).doubleValue());
                    repaymentDetailVo.setTotalMonth(new BigDecimal(commercialLoanEqualLoanVo.getRepaymentList().get(i).getTotalMonth()).setScale(2,RoundingMode.HALF_UP).doubleValue());
                    repaymentDetailVo.setRemainingPrincipal(new BigDecimal(commercialLoanEqualLoanVo.getRepaymentList().get(i).getRemainingPrincipal()).setScale(2,RoundingMode.HALF_UP).doubleValue());
                    repaymentDetailVo.setPeriod(i+1);
                }
                repaymentDetailVoList.add(repaymentDetailVo);
            }

        }else{
            for(int i = 0;i<providentFundLoanEqualLoanVo.getRepaymentList().size();i++){
                RepaymentDetailVo repaymentDetailVo=new RepaymentDetailVo();
                if(i<commercialLoanEqualLoanVo.getRepaymentList().size()){
                    repaymentDetailVo.setPrincipalMonth(new BigDecimal(providentFundLoanEqualLoanVo.getRepaymentList().get(i).getPrincipalMonth()+commercialLoanEqualLoanVo.getRepaymentList().get(i).getPrincipalMonth()).setScale(2,RoundingMode.HALF_UP).doubleValue());
                    repaymentDetailVo.setInterestMonth(new BigDecimal(providentFundLoanEqualLoanVo.getRepaymentList().get(i).getInterestMonth()+commercialLoanEqualLoanVo.getRepaymentList().get(i).getInterestMonth()).setScale(2,RoundingMode.HALF_UP).doubleValue());
                    repaymentDetailVo.setTotalMonth(new BigDecimal(providentFundLoanEqualLoanVo.getRepaymentList().get(i).getTotalMonth()+commercialLoanEqualLoanVo.getRepaymentList().get(i).getTotalMonth()).setScale(2,RoundingMode.HALF_UP).doubleValue());
                    repaymentDetailVo.setRemainingPrincipal(new BigDecimal(providentFundLoanEqualLoanVo.getRepaymentList().get(i).getRemainingPrincipal()+commercialLoanEqualLoanVo.getRepaymentList().get(i).getRemainingPrincipal()).setScale(2,RoundingMode.HALF_UP).doubleValue());
                    repaymentDetailVo.setPeriod(i+1);
                }else{
                    repaymentDetailVo.setPrincipalMonth(new BigDecimal(providentFundLoanEqualLoanVo.getRepaymentList().get(i).getPrincipalMonth()).setScale(2,RoundingMode.HALF_UP).doubleValue());
                    repaymentDetailVo.setInterestMonth(new BigDecimal(providentFundLoanEqualLoanVo.getRepaymentList().get(i).getInterestMonth()).setScale(2,RoundingMode.HALF_UP).doubleValue());
                    repaymentDetailVo.setTotalMonth(new BigDecimal(providentFundLoanEqualLoanVo.getRepaymentList().get(i).getTotalMonth()).setScale(2,RoundingMode.HALF_UP).doubleValue());
                    repaymentDetailVo.setRemainingPrincipal(new BigDecimal(providentFundLoanEqualLoanVo.getRepaymentList().get(i).getRemainingPrincipal()).setScale(2,RoundingMode.HALF_UP).doubleValue());
                    repaymentDetailVo.setPeriod(i+1);
                }
                repaymentDetailVoList.add(repaymentDetailVo);
            }
        }
        totalLoanEqualLoanVo.setRepaymentList(repaymentDetailVoList);
        return totalLoanEqualLoanVo;
    }

    private CommercialLoanVo equalLoanPayment(Double totalAmount, Double loanRate, int totalYear) {
        CommercialLoanVo commercialLoanVo= new CommercialLoanVo();
        commercialLoanVo.setTotalPrincipalAmount(new BigDecimal(totalAmount).setScale(2,RoundingMode.HALF_UP).doubleValue());
        commercialLoanVo.setRepaymentType(2);
        commercialLoanVo.setLoanTerm(totalYear);
        List<RepaymentDetailVo> loanDetailVoList = new ArrayList<>();
        //等额本金方式
        //本金
        BigDecimal amount = new BigDecimal(totalAmount).multiply(new BigDecimal(10000)).setScale(2,RoundingMode.HALF_UP);;;
        //年利率
        BigDecimal rate = new BigDecimal(loanRate).divide(new BigDecimal(100)).setScale(4,RoundingMode.HALF_UP);
        // 或者是贷款年限*12
        //每月应偿还本金
        int totalMonth=totalYear*12;
        BigDecimal monthPay = amount.divide(BigDecimal.valueOf(totalMonth),2, RoundingMode.HALF_UP);
        //剩余本金
        BigDecimal remainAmount = amount;
        //总利息
        BigDecimal totalInterest = new BigDecimal(0);
        for(int month =1;month<=totalMonth;month++){
            //利息
            BigDecimal interest = remainAmount.multiply(rate.divide(BigDecimal.valueOf(12),10,RoundingMode.HALF_UP)).setScale(2,RoundingMode.HALF_UP);
            //最后一期本金调整 避免四舍五入产生的误差
            if(month == totalMonth){
                monthPay = remainAmount;
            }
            //还款金额
            BigDecimal thisMonthPay =  monthPay.add(interest);
            //输出每一期应该偿还的金额
//            System.out.println("第"+month+"个月应还款：" + thisMonthPay);
            totalInterest=totalInterest.add(interest);
            //剩余本金 减去当月已偿还本金
            remainAmount = remainAmount.subtract(monthPay).setScale(2,RoundingMode.HALF_UP);
            RepaymentDetailVo repaymentDetailVo=new RepaymentDetailVo();
            repaymentDetailVo.setPeriod(month);
            repaymentDetailVo.setRemainingPrincipal(remainAmount.setScale(2,RoundingMode.HALF_UP).doubleValue());
            repaymentDetailVo.setPrincipalMonth(monthPay.doubleValue());
            repaymentDetailVo.setInterestMonth(interest.doubleValue());
            repaymentDetailVo.setTotalMonth(thisMonthPay.doubleValue());
            loanDetailVoList.add(repaymentDetailVo);
        }
        commercialLoanVo.setRepaymentList(loanDetailVoList);
        commercialLoanVo.setTotalAmount(amount.add(totalInterest).divide(new BigDecimal(10000)).setScale(2,RoundingMode.HALF_UP).doubleValue());
        commercialLoanVo.setTotalInterestAmount(totalInterest.divide(new BigDecimal(10000)).setScale(2,RoundingMode.HALF_UP).doubleValue());
        commercialLoanVo.setFirstRePayment(loanDetailVoList.get(0).getTotalMonth());
        commercialLoanVo.setLastRePayment(loanDetailVoList.get(loanDetailVoList.size()-1).getTotalMonth());
        if(loanDetailVoList.size()>1) {
            commercialLoanVo.setDecreasingInterest(new BigDecimal(loanDetailVoList.get(0).getInterestMonth()-loanDetailVoList.get(1).getInterestMonth()).setScale(2,RoundingMode.HALF_UP).doubleValue());
        }
        commercialLoanVo.setMonthSalary(new BigDecimal(loanDetailVoList.get(0).getTotalMonth()*2).setScale(2,RoundingMode.HALF_UP).doubleValue());
        commercialLoanVo.setAnnuaSalary(new BigDecimal(commercialLoanVo.getMonthSalary()*12/10000).setScale(2,RoundingMode.HALF_UP).doubleValue());
        commercialLoanVo.setInterestRate(new BigDecimal(commercialLoanVo.getTotalInterestAmount()/commercialLoanVo.getTotalAmount()).setScale(4,RoundingMode.HALF_UP).doubleValue());
        commercialLoanVo.setPrincipalRate(new BigDecimal(1-commercialLoanVo.getInterestRate()).setScale(4,RoundingMode.HALF_UP).doubleValue());
        return commercialLoanVo;
    }

    private CommercialLoanVo equalPrincipalInterest(Double totalAmount, Double loanRate, int totalYear) {
        CommercialLoanVo commercialLoanVo= new CommercialLoanVo();
        commercialLoanVo.setTotalPrincipalAmount(new BigDecimal(totalAmount).setScale(2,RoundingMode.HALF_UP).doubleValue());
        commercialLoanVo.setRepaymentType(1);
        commercialLoanVo.setLoanTerm(totalYear);
        List<RepaymentDetailVo> loanDetailVoList = new ArrayList<>();
        //等额本息方式
        //本金 10W
        BigDecimal amount = new BigDecimal(totalAmount).multiply(new BigDecimal(10000));
        //年利率 3%
        BigDecimal rate = new BigDecimal(loanRate).divide(new BigDecimal(100));
        //固定12期  或者是贷款年限*12
        //月利率 年利率/12 不单独计算出来用变量存储---> 避免除不尽舍入造成误差
        int totalMonth=totalYear*12;
        BigDecimal monthRate = rate.divide(BigDecimal.valueOf(12), 10, RoundingMode.HALF_UP);

        //每月应偿还金额 (等额本息方式)
        BigDecimal factor = BigDecimal.ONE.add(rate.divide(BigDecimal.valueOf(12), 10, RoundingMode.HALF_UP))
                .pow(totalMonth);
        BigDecimal monthPay = amount.multiply(rate.divide(BigDecimal.valueOf(12), 10, RoundingMode.HALF_UP))
                .multiply(factor)
                .divide(factor.subtract(BigDecimal.ONE), 10, RoundingMode.HALF_UP)
                .setScale(2, RoundingMode.HALF_UP);
        //初始化剩余本金
        BigDecimal remainAmount = amount;
        //总利息
        BigDecimal totalInterest = new BigDecimal(0);
        for (int month = 1; month <= totalMonth; month++) {
            //计算当期利息
            BigDecimal interest = remainAmount.multiply(monthRate)
                    .setScale(2, RoundingMode.HALF_UP);

            //计算当期本金 (最后一期特殊处理)
            BigDecimal principal;
            if (month == totalMonth) {
                principal = remainAmount;
            } else {
                principal = monthPay.subtract(interest).setScale(2, RoundingMode.HALF_UP);
            }

            //当期总还款
            BigDecimal pay = principal.add(interest);
//            System.out.println("第" + month + "个月还款：" + pay);

            //更新剩余本金
            remainAmount = remainAmount.subtract(principal);
            totalInterest=totalInterest.add(interest);
            RepaymentDetailVo repaymentDetailVo=new RepaymentDetailVo();
            repaymentDetailVo.setPeriod(month);
            repaymentDetailVo.setRemainingPrincipal(remainAmount.setScale(2,RoundingMode.HALF_UP).doubleValue());
            repaymentDetailVo.setPrincipalMonth(principal.doubleValue());
            repaymentDetailVo.setInterestMonth(interest.doubleValue());
            repaymentDetailVo.setTotalMonth(monthPay.doubleValue());
            loanDetailVoList.add(repaymentDetailVo);
        }
        commercialLoanVo.setRepaymentList(loanDetailVoList);
        commercialLoanVo.setTotalAmount(amount.add(totalInterest).divide(new BigDecimal(10000)).setScale(2,RoundingMode.HALF_UP).doubleValue());
        commercialLoanVo.setTotalInterestAmount(totalInterest.divide(new BigDecimal(10000)).setScale(2,RoundingMode.HALF_UP).doubleValue());
        commercialLoanVo.setFirstRePayment(loanDetailVoList.get(0).getTotalMonth());
        commercialLoanVo.setInterestRate(new BigDecimal(commercialLoanVo.getTotalInterestAmount()/commercialLoanVo.getTotalAmount()).setScale(4,RoundingMode.HALF_UP).doubleValue());
        commercialLoanVo.setPrincipalRate(new BigDecimal(1-commercialLoanVo.getInterestRate()).setScale(4,RoundingMode.HALF_UP).doubleValue());
        commercialLoanVo.setMonthSalary(new BigDecimal(loanDetailVoList.get(0).getTotalMonth()*2).setScale(2,RoundingMode.HALF_UP).doubleValue());
        commercialLoanVo.setAnnuaSalary(new BigDecimal(commercialLoanVo.getMonthSalary()*12/10000).setScale(2,RoundingMode.HALF_UP).doubleValue());
        return commercialLoanVo;
    }
}
